import base64
import pickle

from django.shortcuts import render
from django_redis import get_redis_connection
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

from .serialziers import CartSerializer, CartSKUSerializer, CartDeletaeSerializer, CartSelectAllSerializer
from goods.models import SKU
from .utils import merge_cart_cookie_to_redis


class CartSelectAllView(APIView):
    """购物车全选"""

    def perform_authentication(self, request):
        """重写父类用户的验证方法,不在进入视图前检查JWT"""
        pass

    def put(self, request):
        """是否全选"""
        serializer = CartSelectAllSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        selected = serializer.validated_data['selected']

        try:
            user = request.user
        except:
            user = None
        response = Response({'message': 'OK'})
        if user is not None and user.is_authenticated:
            # 用户已登录操作redis
            # 连接redis
            redis_conn = get_redis_connection('cart')
            # 查询出所有的购物车数据
            redis_cart_dict = redis_conn.hgetall('cart_%s' % user.id)
            # 取出所有的sku_id
            sku_ids = redis_cart_dict.keys()
            # 将所有的sku_id 添中到选中状态集合中
            if selected:
                redis_conn.sadd('selected_%s' % user.id, *sku_ids)
            else:
                # 装饰所有的sku_id 从选中状态集合中删除
                redis_conn.srem('selected_%s' % user.id, *sku_ids)
            # return Response({'message': 'OK'})
        else:
            # 用户未登录操作cookie
            cart_str = request.COOKIES.get('cart')
            # 读取cookie中的购物车数据
            if cart_str:
                cart_str_bytes = cart_str.encode()
                cart_dict_bytes = base64.b64decode(cart_str_bytes)
                cart_dict = pickle.loads(cart_dict_bytes)
            else:
                cart_dict = {}

            # 遍历购物车字典将所有的sku_id对应的selected设置为用户传入的selected
            for sku_id in cart_dict:
                cart_dict[sku_id]['selected'] = selected
            # 构造购物车字符串
            cookie_cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
            # cookie中写入购物车字符串
            response.set_cookie('cart', cookie_cart_str)

        return response


# Create your views here.
class CartView(APIView):
    """操作购物车:增删改查"""

    def perform_authentication(self, request):
        """
        重写父类的用户验证方法,不在进入视图前就检查JWT保证用户未登录也可以进入下面的请求方法
        """
        pass

    def post(self, request):
        """添加购物车"""
        # 创建序列化器对象并验证字段
        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        sku_id = serializer.validated_data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.validated_data.get('selected')

        # 获取user, 用于判断用户是否登录
        try:
            user = request.user
        except Exception:
            user = None
        if user is not None and user.is_authenticated:
            # 用户已登录,操作redis购物车
            redis_conn = get_redis_connection('cart')
            # 创建管道操作redis,提升访问数据库效率
            pl = redis_conn.pipeline()
            # 新增购物车数据
            pl.hincrby('cart_%s' % user.id, sku_id, count)
            # 新增选中的状态
            if selected:
                pl.sadd('selected_%s' % user.id, sku_id)
            # 执行管道
            pl.execute()
            # 响应结果
            return Response(serializer.data, status=status.HTTP_201_CREATED)

        else:
            # 用户未登录,操作cookie购物车
            cart_str = request.COOKIES.get('cart')  # 获取购物车数据
            if cart_str:  # 用户操作过cookie购物车
                # 将cart_str转成bytes,
                cart_bytes = cart_str.encode()
                # 再将bytes转成base64的bytes
                cart_base64 = base64.b64decode(cart_bytes)
                # 最后将bytes转字典
                cart_dict = pickle.loads(cart_base64)
            else:  # 用户没有操作过cookie购物车
                cart_dict = {}

            # 判断要加入购物车的商品是否已经在购物车中
            # 如果相同商品,累加求各,反之,直接赋值
            if sku_id in cart_dict:
                # 累加求和
                # origin_count = cart_dict[sku_id]['count']
                # count += origin_count
                cart_dict[sku_id]['count'] += count
            else:
                cart_dict[sku_id] = {
                    'count': count,
                    'selected': selected
                }
            # 将字典转成bytes, 再将bytes转换成base64的bytes, 最后将bytes转字符串
            cart_dict_bytes = pickle.dumps(cart_dict)
            cart_dict_64 = base64.b64encode(cart_dict_bytes)
            cookie_cart_str = cart_dict_64.decode()

            response = Response(serializer.data, status.HTTP_201_CREATED)

            # 响应结果并将购物车数据写入到cookie
            response.set_cookie('cart', cookie_cart_str)
            return response

    def get(self, request):
        """读取购物车"""
        # 获取user,用于判断用户是否登录
        try:
            user = request.user
        except Exception:
            user = None
        response = Response()
        # 判断用户是否登录
        if user is not None and user.is_authenticated:
            # 用户已登录,操作redis购物车
            # 创建连接到redis对象
            # 全并----
            redis_cart, redis_selected = merge_cart_cookie_to_redis(request, user)

            # redis_conn = get_redis_connection('cart')
            # redis_cart = redis_conn.hgetall('cart_%s' % user.id)
            # # 获取redis中的选中状态
            # redis_selected = redis_conn.smembers('selected_%s' % user.id)
            # 将redis中的两个数据统一格式,跟cookie中的格式一致,方便统一查询
            cart_dict = {}
            for sku_id, count in redis_cart.items():
                cart_dict[int(sku_id)] = {
                    'count': int(count),
                    'selected': sku_id in redis_selected
                }
            response.delete_cookie('cart')

        else:
            # 用户未登录,操作cookie购物车
            cart_str = request.COOKIES.get('cart')
            if cart_str:  # 用户操作过cookie购物车
                # 将cart_str转成bytes,
                cart_bytes = cart_str.encode()
                # 再将bytes转成base64的bytes
                cart_base64 = base64.b64decode(cart_bytes)
                # 最后将bytes转字典
                cart_dict = pickle.loads(cart_base64)
            else:  # 用户从没操作过cookie购物车
                cart_dict = {}

        if cart_dict:  # 有购物车数据

            # 查询购物车数据
            sku_ids = cart_dict.keys()
            skus = SKU.objects.filter(id__in=sku_ids)
            # 补充count和selected字段
            for sku in skus:
                sku.count = cart_dict[sku.id]['count']
                sku.selected = cart_dict[sku.id]['selected']
            # 创建序列化器序列化商品数据
            serializer = CartSKUSerializer(skus, many=True)
            response.data = serializer.data
            # 响应结果
            return response
        return Response(status=status.HTTP_204_NO_CONTENT)

    def put(self, request):
        """修改购物车"""
        # 创建序列化器对象并验证字段
        serializer = CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        sku_id = serializer.validated_data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.validated_data.get('selected')

        # 获取user, 用于判断用户是否登录
        try:
            user = request.user
        except Exception:
            user = None
        if user is not None and user.is_authenticated:
            # 用户已登录,操作redis购物车
            redis_conn = get_redis_connection('cart')
            # 创建管道操作redis,提升访问数据库效率
            pl = redis_conn.pipeline()
            # 因为接口设计为幂等的,直接覆盖
            pl.hset('cart_%s' % user.id, sku_id, count)
            # 是否选中的状态
            if selected:
                pl.sadd('selected_%s' % user.id, sku_id)  # 添加
            else:
                pl.srem('selected_%s' % user.id, sku_id)  # 删除
            # 执行管道
            pl.execute()
            # 响应结果
            return Response(serializer.data)

        else:
            # 用户未登录,操作cookie购物车
            cart_str = request.COOKIES.get('cart')  # 获取购物车数据
            if cart_str:  # 用户操作过cookie购物车
                # 将cart_str转成bytes,
                cart_bytes = cart_str.encode()
                # 再将bytes转成base64的bytes
                cart_base64 = base64.b64decode(cart_bytes)
                # 最后将bytes转字典
                cart_dict = pickle.loads(cart_base64)
            else:  # 用户没有操作过cookie购物车
                cart_dict = {}

            # 因为接口设计为幂等的, 直接覆盖
            cart_dict[sku_id] = {
                'count': count,
                'selected': selected
            }
            # 将字典转成bytes, 再将bytes转换成base64的bytes, 最后将bytes转字符串
            cart_dict_bytes = pickle.dumps(cart_dict)
            cart_dict_64 = base64.b64encode(cart_dict_bytes)
            cookie_cart_str = cart_dict_64.decode()

            response = Response(serializer.data, status.HTTP_201_CREATED)

            # 响应结果并将购物车数据写入到cookie
            response.set_cookie('cart', cookie_cart_str)
            return response

    def delete(self, request):
        """删除购物车"""
        # 创建序列化器对象并验证字段
        serializer = CartDeletaeSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # 获取校验后的参数
        sku_id = serializer.validated_data.get('sku_id')

        # 获取user,用于判断用户是否登录
        try:
            user = request.user
        except Exception:
            user = None

        # 判断用户是否登录
        if user is not None and user.is_authenticated:
            # 用户已登录,操作redis购物车
            # 创建连接到redis对象
            redis_conn = get_redis_connection('cart')
            # 管道
            pl = redis_conn.pipeline()
            # 删除键,就等于删除了整条记录
            pl.hdel('cart_%s' % user.id, sku_id)
            pl.srem('selected_%s' % user.id, sku_id)
            pl.execute()
            return Response(status=status.HTTP_204_NO_CONTENT)
        else:  # 用户未登录,操作cookie购物车
            cart_str = request.COOKIES.get('cart')

            if cart_str:
                # 转换回字典
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                cart_dict = {}
            response = Response(status=status.HTTP_204_NO_CONTENT)
            if sku_id in cart_dict:
                del cart_dict[sku_id]
                cookie_cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()
                response.set_cookie('cart', cookie_cart_str)
            return response
